/*
 *  SBJ/NXT/Device.cpp
 *  NXTFace
 *
 *  Created by David Giovannini on 8/23/06.
 *  Copyright 2006 Software by Jove. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 */

#include "SBJ/NXT/Device.h"

#include "SBJ/NXT/Command.h"
#include "SBJ/NXT/CommandTraits.h"

#include "fantom/iNXT.h"
#include "fantom/tStatus.h"

namespace SBJ
{

namespace NXT
{

Device::Device( const std::string& resourceName, nFANTOM100::iNXT* opaqueRep )
: m_opaqueRef( opaqueRep )
, m_connectInfo( resourceName )
, m_outputA( *this, out_A )
, m_outputB( *this, out_B )
, m_outputC( *this, out_C )
, m_input1( *this, in_1 )
, m_input2( *this, in_2 )
, m_input3( *this, in_3 )
, m_input4( *this, in_4 )
{
	if ( m_opaqueRef == 0 ) return;// false;
	
	nFANTOM100::tStatus status;
	
	ViUInt8 protocolVersionMajor = 0;
	ViUInt8 protocolVersionMinor = 0;
	ViUInt8 firmwareVersionMajor = 0;
	ViUInt8 firmwareVersionMinor = 0;
	
	m_opaqueRef->getFirmwareVersion( protocolVersionMajor, protocolVersionMinor,
			firmwareVersionMajor, firmwareVersionMinor, status );
			
	m_version = DeviceVersion(	protocolVersionMajor,
								protocolVersionMinor,
								firmwareVersionMajor,
								firmwareVersionMinor );
									
	//if ( m_version.isEmpty() ) return false;
	
	//if ( m_connectInfo.btConnected &&  m_connectInfo.btName.empty() ) return false;

	//return true;
}

Device::~Device( void )
{
	if ( m_opaqueRef ) nFANTOM100::iNXT::destroyNXT( m_opaqueRef );
}
	
bool
Device::isBluetooth( void ) const
{
	return m_connectInfo.btConnected;
}
	
Device::operator bool ( void ) const
{
	return ( m_opaqueRef && m_version.isEmpty() == false && ( m_connectInfo.btConnected || m_connectInfo.btName.empty() == false ) );
}

const DeviceVersion&
Device::getVersion( void ) const
{
	return m_version;
}

const std::string&
Device::getSessionName( void ) const
{
	return m_connectInfo.deviceSessionName;
}

std::string
Device::getName( void ) const
{
	if ( m_opaqueRef != 0 )
	{
		ViChar name[ 16 ] = { 0 };
		ViByte bluetoothadress[ 7 ] = { 0, 0, 0, 0, 0, 0, 0 };
		ViUInt8 signalStrength[ 4 ] = { 0, 0, 0, 0 };
		ViUInt32 freeSpace = 0;
		nFANTOM100::tStatus status;
		m_opaqueRef->getDeviceInfo( name, bluetoothadress, signalStrength, freeSpace, status );
		return name;
	}
	return std::string();
}

Error
Device::setName( const std::string& name )
{
	if ( m_opaqueRef != 0 )
	{
		nFANTOM100::tStatus status;
		m_opaqueRef->setName( name.c_str(), status );
		return status.getCode();
	}
	return nFANTOM100::kStatusFWUnknownErrorCode;
}

bool
Device::connect( const std::string& passKey )
{
	// Pair up bluetooth if we can
	nFANTOM100::tStatus status;
	if ( isBluetooth() )
	{
		bool isPaired = nFANTOM100::iNXT::isPaired( m_connectInfo.deviceSessionName.c_str(), status );
		if ( isPaired == true && passKey != m_connectInfo.btPasskey )
		{
			nFANTOM100::iNXT::unpairBluetooth( m_connectInfo.deviceSessionName.c_str(), status );
		}
		if ( status.isNotFatal() && ( isPaired == false || passKey != m_connectInfo.btPasskey ) )
		{
			ViChar pairedResourceName[ 256 ];
			nFANTOM100::iNXT::pairBluetooth( m_connectInfo.deviceSessionName.c_str(), passKey.c_str(), pairedResourceName, status );
			if ( status.isNotFatal() )
			{
				m_connectInfo.btName = pairedResourceName;
				return true;
			}
		}
		return false;
	}
	return true;
}

DeviceInfo
Device::getInfo( void ) const
{
	if ( m_opaqueRef == 0 )
	{
		return DeviceInfo();
	}
			
	ViChar name[ 16 ] = { 0 };
	ViByte bluetoothadress[ 7 ] = { 0, 0, 0, 0, 0, 0, 0 };
	ViUInt8 signalStrength[ 4 ] = { 0, 0, 0, 0 };
	ViUInt32 freeSpace = 0;
	nFANTOM100::tStatus status;
	m_opaqueRef->getDeviceInfo( name, bluetoothadress, signalStrength, freeSpace, status );
	
	if ( status.isNotFatal() )
	{
		DeviceInfo info =
		{
			m_version,
			m_connectInfo,
			name,
			(const char *)bluetoothadress, 
			{ signalStrength[ 0 ], signalStrength[ 1 ], signalStrength[ 2 ], signalStrength[ 3] },
			freeSpace, status.getCode()
		};
		return info;
	}
	return DeviceInfo();
}

double
Device::getBatteryLevel( void )
{
	if ( !m_opaqueRef ) return 0.0;
	return (double)Command< GetBatteryLevel >( *this ).result() / 10000.0;
}

std::string
Device::getProgramName( void )
{
	if ( !m_opaqueRef ) return std::string();
	Command< GetProgramName > command( *this );
	return (const char*)command.result();
}
	
Output&
Device::getOutput( OutputPort port )
{
	switch ( port )
	{
		case out_A:
			return m_outputA;
		case out_B:
			return m_outputB;
		case out_C:
			return m_outputC;
	}
	throw 0;
}
	
Input&
Device::getInput( InputPort port )
{
	switch ( port )
	{
		case in_1:
			return m_input1;
		case in_2:
			return m_input2;
		case in_3:
			return m_input3;
		case in_4:
			return m_input4;
	}
	throw 0;
}

void
Device::runProgram( const std::string& program )
{
	if ( !m_opaqueRef ) return;
	stopProgram();
	std::string fullName = program;
	if ( program.find_last_of( ".rxe" ) != program.size() - 4 )
	{
		fullName += ".rxe";
	}
	Command< StartProgram > command( *this, fullName );
}

void
Device::stopProgram( void )
{
	if ( !m_opaqueRef ) return;
	Command< StopProgram > command( *this );
}
	
void
Device::playTone( unsigned short hrz, double secs )
{
	if ( !m_opaqueRef ) return;
	PlayTone::Params params = { hrz, secs * 1000.0 };
	Command< PlayTone > command( *this, params );
}

void
Device::sendMessage( const std::string& msg, unsigned char box )
{
	if ( !m_opaqueRef ) return;
	WriteMessage::Params params = { box, msg };
	Command< WriteMessage > command( *this, params );
}

std::string
Device::waitForMessage( unsigned char box, bool clear )
{
	std::string result;
	if ( m_opaqueRef )
	{
		do
		{
			result = getLocalMessage( box, clear );
		} while ( result.empty() == true );
	}
	return result;
}

std::string
Device::getLocalMessage( unsigned char box, bool clear )
{
	if ( !m_opaqueRef ) return std::string();
	ReadMessage::Params params = { 0, box, clear };
	Command< ReadMessage > command( *this, params );
	return (const char *)command.result().msg;
}

std::string
Device::getRemoteMessage( unsigned char box, bool clear )
{
	if ( !m_opaqueRef ) return std::string();
	ReadMessage::Params params = { box + 10, box, clear };
	Command< ReadMessage > command( *this, params );
	return (const char *)command.result().msg;
}
	
Error
Device::sendCommand( const char* input, size_t inputSize, char* output, size_t outputSize )
{
	if ( m_opaqueRef != 0 )
	{
		nFANTOM100::tStatus status;
		m_opaqueRef->sendDirectCommand(
			( outputSize > 0 ? VI_TRUE : VI_FALSE ), 
			reinterpret_cast< const ViByte* >( input ), inputSize,  
			( outputSize > 0 ) ? reinterpret_cast< ViByte* >( output ) : 0, outputSize, 
			status );
		return status.getCode();
	}
	return nFANTOM100::kStatusFWUnknownErrorCode;
}

void
print( const DeviceInfo& info )
{
	printf( "Name: %s\n", info.name.c_str() );
	printf( "Session Name: %s\n", info.connect.deviceSessionName.c_str() );
	printf( "Bluetooth connect: %s\n", (info.connect.btConnected ? "yes" : "no") );
	if ( info.connect.btConnected )
	{
	printf( "Bluetooth Name: %s, %s\n", info.connect.btName.c_str(), info.connect.btPasskey.c_str() );
	printf( "Bluetooth Address: %\ns", info.bluetoothAddress.c_str() );
	printf( "Bluetooth Signal Strength: %d.%d.%d.%d\n", info.signalStrength[ 0 ], info.signalStrength[ 1 ], info.signalStrength[ 2 ], info.signalStrength[ 3 ] );
	}
	printf( "Version %s\n", info.devVers.toString().c_str() );
	printf( "Free Space: %d\n\n", info.freeSpace );
}

}

}
